package com.jeesite.modules.digitalize.util.minio;

import cn.hutool.core.date.DateUtil;
import com.jeesite.modules.digitalize.util.response.ResultStatusCode;
import com.jeesite.modules.digitalize.util.response.ResultVo;
import io.minio.*;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.Date;
import io.minio.errors.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.UUID;

/**
 * @author patronli
 * @Description: minio文件操作测试
 * @date 2023年08月17日 14:06:12
 */
@Configuration
@Slf4j
public class MinioFileUtil {
    @Resource
    private MinioProperties minioProperties;

    private MinioClient minioClient;

    private MinioClient minioPreviewClient;



    /**
     * 下面这个和上面的意思差不多，但是这个是新版本
     * 获取一个连接minio服务端的客户端
     *
     * @return MinioClient
     */
    @Bean
    public MinioClient getClient() throws Exception {
        String url = "http://" + minioProperties.getIp() + ":" + minioProperties.getPort();
        MinioClient minioClient = new MinioClient(url, minioProperties.getAccess(), minioProperties.getSecret());
        this.minioClient = minioClient;
        return minioClient;
    }

    @Bean
    public MinioClient getPreviewClient() throws Exception {
        String url = "http://" + minioProperties.getPreviewIp() + ":" + minioProperties.getPort();
        MinioClient minioClient = new MinioClient(url, minioProperties.getAccess(), minioProperties.getSecret());
        this.minioPreviewClient = minioClient;
        return minioPreviewClient;
    }

    /**
     * 删除文件夹
     *
     * @param objectName 文件夹名done
     * @return
     */
    public ResultVo deleteBucketFile(String objectName) {
        try {
            minioClient.removeObject(minioProperties.getBucketName(), objectName);
            return ResultVo.success();
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultVo.failure(ResultStatusCode.FILE_DEL_FAIL.getCode(), ResultStatusCode.FILE_DEL_FAIL.getMsg());
        }
    }


    /**
     * 检测某个桶内是否存在某个文件
     *
     * @param objectName 文件名称
     * @param bucketName 桶名称
     */
    public boolean getBucketFileExist(String objectName, String bucketName) throws Exception {
        if (!StringUtils.hasLength(objectName) || !StringUtils.hasLength(bucketName)) {
            throw new RuntimeException("检测文件的时候，文件名和桶名不能为空！");
        }
        try {
            // 判断文件是否存在
            boolean exists = minioClient.bucketExists(bucketName) &&
                    minioClient.statObject(bucketName,objectName) != null;
            return exists;
        } catch (ErrorResponseException e) {
            log.info("文件不存在 ! Object does not exist");
            return false;
        } catch (Exception e) {
            throw new Exception(e);
        }
    }



    /**
     * 根据MultipartFile file上传文件
     * minio 采用文件流上传，可以换成下面的文件上传
     *
     * @param file       上传的文件
     */
    public ResultVo uploadFile(MultipartFile file) {
        try {
            if (file == null || file.getSize() == 0 || file.isEmpty()) {
                throw new RuntimeException("上传文件为空，请重新上传");
            }

            // 获取上传的文件名
            Date date = new Date();
            String route = DateUtil.year(date) + "/" + DateUtil.month(date) + "/" + DateUtil.dayOfMonth(date) + "/" + UUID.randomUUID().toString().replaceAll("-", "");
            String filename = file.getOriginalFilename();
            assert filename != null;
            //可以选择生成一个minio中存储的文件名称
            String minioFilename = route + "/" + filename;

            InputStream inputStream = file.getInputStream();
            long size = file.getSize();
            String contentType = file.getContentType();
            System.out.println(contentType);
            // Upload known sized input stream.
            PutObjectOptions putObjectOptions =
                    new PutObjectOptions(size, PutObjectOptions.MIN_MULTIPART_SIZE);
            // 文件的ContentType
            putObjectOptions.setContentType(contentType);
            minioClient.putObject(minioProperties.getBucketName(), minioFilename, inputStream, putObjectOptions);
            if (this.getBucketFileExist(minioFilename, minioProperties.getBucketName())) { //已经上传成功
                return ResultVo.success(minioFilename);
            } else {//没有上传成功
                return ResultVo.failure(ResultStatusCode.FILE_UPLOAD_FAIL.getCode(), ResultStatusCode.FILE_UPLOAD_FAIL.getMsg());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ResultVo.failure(ResultStatusCode.FILE_UPLOAD_FAIL.getCode(), ResultStatusCode.FILE_UPLOAD_FAIL.getMsg());
        }
    }



    /**
     * 文件下载,通过http返回， 浏览器能解析则为预览 不能解析则为下载
     *
     * @param response   http请求的响应对象
     * @param objectName 下载的文件名称
     */
    public void downloadFile(HttpServletResponse response, String objectName) {
        try {
            //获取一个下载的文件输入流操作
            InputStream objectResponse = minioClient.getObject(
                    minioProperties.getBucketName(),
                    objectName);
            OutputStream outputStream = response.getOutputStream();
            int len = 0;
            byte[] buf = new byte[1024 * 8];
            while ((len = objectResponse.read(buf)) != -1) {
                outputStream.write(buf, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * @date 2023年12月29日 11:13:31
     * @author patronli
     * @Description: 获取文件大小 done
     */
    public Integer getFileSize(String fileName){
        Integer memory = 0;
        try {
            ObjectStat os = minioClient.statObject(minioProperties.getBucketName(), fileName);
            System.out.println(os.length());
            memory = (int)os.length();
            memory = (int) Math.ceil(memory / 1024.0);
        } catch (Exception e) {
            log.error("获取文件大小失败:{}", e.getMessage(), e);
        }
        return memory;
    }

    /**
     * @date 2023年12月29日 11:12:56
     * @author patronli
     * @Description: 获取全局预览地址 done
     */
    public String getPreviewFileUrl(String fileName) {
        try {
            String objectUrl = "";
            if (fileName!=null) {
                objectUrl = minioPreviewClient.presignedGetObject(minioProperties.getBucketName(),fileName);
            }
            return objectUrl;
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }



    /**
     * 创建桶
     *
     * @param bucketName 桶名称
     */
//    public void createBucket(String bucketName) throws Exception {
//        if (!StringUtils.hasLength(bucketName)) {
//            throw new RuntimeException("创建桶的时候，桶名不能为空！");
//        }
//        minioClient.makeBucket(MakeBucketArgs.builder()
//                .bucket(bucketName)
//                .build());
//    }



    /**
     * 检查桶是否存在
     *
     * @param bucketName 桶名称
     * @return boolean true-存在 false-不存在
     */
//    public boolean checkBucketExist(String bucketName) throws Exception {
//        if (!StringUtils.hasLength(bucketName)) {
//            throw new RuntimeException("检测桶的时候，桶名不能为空！");
//        }
//        return minioClient.bucketExists(bucketName);
//    }

    /**
     * @date 2023年08月30日 16:01:02
     * @author patronli
     * @return:
     * @Description: 模拟下载文件夹
     */

//    public void downloadFolder(HttpServletResponse response) throws Exception {
//        List<String> pathList = new ArrayList<>();
////        pathList.add("2023/7/23/a70e7906e24240f98c811d420477b28d/许可文件申请及使用须知.png");
////        pathList.add("2023/7/23/b43ea0883f164df48caab28ecc683eb9/极智构件工厂JZCF-V3.6.0 20230208.exe");
////        pathList.add("2023/7/23/bf4b4ecc943643b9836342526ffe4b19/CustomApiConfig.java");
//
//        pathList.add("2023/7/23/a70e7906e24240f98c811d420477b28d/");
//        pathList.add("2023/7/23/b43ea0883f164df48caab28ecc683eb9/");
//        pathList.add("2023/7/23/bf4b4ecc943643b9836342526ffe4b19/");
//
//        response.reset();
//        response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("测试.zip", "UTF-8"));
//        response.setCharacterEncoding("utf-8");
//        response.setContentType("application/octet-stream");
//
//        try(ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()))) {
//            for (String objectName : pathList) {
//                zipOutputStream.putNextEntry(new ZipEntry(objectName));
//
////                InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
//////                String fileName = objectName.substring(objectName.lastIndexOf("/")+1);
////                try (BufferedInputStream bis = new BufferedInputStream(inputStream)) {
////                    byte[] bytes = new byte[1024];
////                    int i = 0;
////                    while ((i = bis.read(bytes)) != -1) {
////                        zipOutputStream.write(bytes, 0, i);
////                    }
////                } catch (Exception e) {
////                    e.printStackTrace();
////                }
//            }
//            zipOutputStream.closeEntry();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }


    /**
     * 文件下载到指定路径
     *
     * @param downloadPath 下载到本地路径
     * @param bucketName   下载指定服务器的桶名称
     * @param objectName   下载的文件名称
     */
//    public void downloadPath(String downloadPath, String bucketName, String objectName) throws Exception {
//        if (downloadPath.isEmpty() || !StringUtils.hasLength(bucketName) || !StringUtils.hasLength(objectName)) {
//            throw new RuntimeException("下载文件参数不全！");
//        }
//
//        if (!new File(downloadPath).isDirectory()) {
//            throw new RuntimeException("本地下载路径必须是一个文件夹或者文件路径！");
//        }
//
//        if (!this.checkBucketExist(bucketName)) {
//            throw new RuntimeException("当前操作的桶不存在！");
//        }
//
//        downloadPath += objectName;
//
//        minioClient.downloadObject(
//                DownloadObjectArgs.builder()
//                        .bucket(bucketName) //指定是在哪一个桶下载
//                        .object(objectName)//是minio中文件存储的名字;本地上传的文件是user.xlsx到minio中存储的是user-minio,那么这里就是user-minio
//                        .filename(downloadPath)//需要下载到本地的路径，一定是带上保存的文件名；如 d:\\minio\\user.xlsx
//                        .build());
//    }


    /**
     * @date 2023年08月17日 14:02:35
     * @author patronli
     * @Description: main测试
     */
//    public static void main(String[] args) {
//        System.out.println(UUID.randomUUID().toString().replaceAll("-", ""));

//        try {
//            // 创建客户端
//            MinioClient minioClient =
//                    MinioClient.builder()
//                            // api地址
////                            .endpoint("42.194.190.49", 9000, false)
//                            .endpoint("http://127.0.0.1:9000")
//                            // 前面设置的账号密码
//                            .credentials("minioadmin", "minioadmin")
//                            .build();
//
//            System.out.println(minioClient);
//             //检查桶是否存在
//            boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket("test").build());
//            if (!found) {
//                // 创建桶
//                minioClient.makeBucket(MakeBucketArgs.builder().bucket("test").build());
//            }
//
//            //列出所有桶名
//            List<Bucket> buckets = minioClient.listBuckets();
//            for (Bucket i : buckets) {
//                System.out.println(i.name());
//            }
//
//            System.out.println("开始你的操作");
//
//            File file = new File("C:\\Users\\NINGMEI\\Desktop\\音乐\\微信截图_20230816095521.png");
//
//            String fileName = System.currentTimeMillis()+"/"+file.getName();
//            String realFileName = fileName.substring(fileName.lastIndexOf("\\") + 1, fileName.lastIndexOf("."));
//            String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
//
//
//            //通过文件格式上传一个文件
//            InputStream fileInputStream = new FileInputStream(file);
//            long size = file.length();
//
//            minioClient.putObject(
//                    PutObjectArgs.builder()
//                            .bucket("test")
//                            .object(fileName)
//                            .stream(fileInputStream, size, -1)
////                            .contentType("image/png")
//                            .build());
//
//            //文件下载，都是这种下载到指定路径
//            minioClient.downloadObject(DownloadObjectArgs.builder()
//                    .bucket("test")
//                    .object(fileName)
//                    .filename("D:\\"+file.getName())
//                    .build());
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

    /**
     * @param response
     * @param bucketName
     * @param objectName
     * @date 2023年08月23日 16:37:53
     * @author patronli
     * @return: void
     * @Description: 此方法，仅能下载，不支持图片pdf预览
     */
//    public void onlyDownloadFile(HttpServletResponse response, String bucketName, String objectName) throws Exception {
//        InputStream inputStream = null;
//        OutputStream outputStream = null;
//        try {
//            if (StringUtils.isEmpty(objectName)) {
//                response.setHeader("Content-type", "text/html;charset=UTF-8");
//                String data = "文件下载失败";
//                OutputStream ps = response.getOutputStream();
//                ps.write(data.getBytes("UTF-8"));
//                return;
//            }
//
//            outputStream = response.getOutputStream();
//            // 获取文件对象
//            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
//            byte buf[] = new byte[1024];
//            int length = 0;
//            response.reset();
//            response.setHeader("Content-Disposition", "attachment;filename=" +
//                    URLEncoder.encode(objectName.substring(objectName.lastIndexOf("/") + 1), "UTF-8"));
//            response.setContentType("application/octet-stream");
//            response.setCharacterEncoding("UTF-8");
//            // 输出文件
//            while ((length = inputStream.read(buf)) > 0) {
//                outputStream.write(buf, 0, length);
//            }
//            System.out.println("下载成功");
//            inputStream.close();
//        } catch (Throwable ex) {
//            response.setHeader("Content-type", "text/html;charset=UTF-8");
//            String data = "文件下载失败";
//            try {
//                OutputStream ps = response.getOutputStream();
//                ps.write(data.getBytes("UTF-8"));
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        } finally {
//            try {
//                outputStream.close();
//                if (inputStream != null) {
//                    inputStream.close();
//                }
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }
//    }

    /**
     * 上传本地文件，根据路径上传
     * minio 采用文件内容上传，可以换成上面的流上传
     *
     * @param filePath 上传本地文件路径
     * @Param bucketName 上传至服务器的桶名称
     */
//    public boolean uploadPath(String filePath, String bucketName) throws Exception {
//
//        File file = new File(filePath);
//        if (!file.isFile()) {
//            throw new RuntimeException("上传文件为空，请重新上传");
//        }
//
//        if (!StringUtils.hasLength(bucketName)) {
//            log.info("传入桶名为空，将设置默认桶名：" + minioProperties.getBucketName());
//            bucketName = minioProperties.getBucketName();
//            if (!this.checkBucketExist(minioProperties.getBucketName())) {
//                this.createBucket(minioProperties.getBucketName());
//            }
//        }
//
//        if (!this.checkBucketExist(bucketName)) {
//            throw new RuntimeException("当前操作的桶不存在！");
//        }
//
//        String minioFilename = UUID.randomUUID().toString() + "_" + file.getName();//获取文件名称
//        String fileType = minioFilename.substring(minioFilename.lastIndexOf(".") + 1);
//
//        minioClient.uploadObject(
//                UploadObjectArgs.builder()
//                        .bucket(bucketName)
//                        .object(minioFilename)//文件存储在minio中的名字
//                        .filename(filePath)//上传本地文件存储的路径
//                        .contentType(fileType)//文件类型
//                        .build());
//
//        return this.getBucketFileExist(minioFilename, bucketName);
//    }

    /**
     * 判断桶中是否存在文件夹
     *
     * @param bucketName 同名称
     * @param objectName 文件夹名称
     * @param isDeep     是否递归查询(暂不支持)
     * @return
     */
//    public Boolean checkBucketFolderExist(String bucketName, String objectName, Boolean isDeep) {
//        Iterable<Result<Item>> results = minioClient.listObjects(
//                ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(isDeep).build());
//
//        return results.iterator().hasNext(); // 文件夹下存在文件
//    }


    //    /**
//     * 创建桶,固定minio容器
//     *
//     * @param bucketName 桶名称
//     */
//    public void createBucketByRegion(String bucketName, String region) throws Exception {
//        if (!StringUtils.hasLength(bucketName)) {
//            throw new RuntimeException("创建桶的时候，桶名不能为空！");
//        }
//        MinioClient minioClient = this.getClient();
//
//        // Create bucket with specific region.
//        minioClient.makeBucket(MakeBucketArgs.builder()
//                .bucket(bucketName)
//                .region(region) //
//                .build());
//
////        // Create object-lock enabled bucket with specific region.
////        minioClient.makeBucket(
////                MakeBucketArgs.builder()
////                        .bucket("my-bucketname")
////                        .region("us-west-1")
////                        .objectLock(true)
////                        .build());
//    }

    /**
     * 修改桶名
     * (minio不支持直接修改桶名，但是可以通过复制到一个新的桶里面，然后删除老的桶)
     *
     * @param oldBucketName 桶名称
     * @param newBucketName 桶名称
     */
//    public void renameBucket(String oldBucketName, String newBucketName) throws Exception {
//        if (!StringUtils.hasLength(oldBucketName) || !StringUtils.hasLength(newBucketName)) {
//            throw new RuntimeException("修改桶名的时候，桶名不能为空！");
//        }
//
//    }

    /**
     * 删除桶
     *
     * @param bucketName 桶名称
     */
//    public void deleteBucket(String bucketName) throws Exception {
//        if (!StringUtils.hasLength(bucketName)) {
//            throw new RuntimeException("删除桶的时候，桶名不能为空！");
//        }
//
//        minioClient.removeBucket(
//                RemoveBucketArgs.builder()
//                        .bucket(bucketName)
//                        .build());
//    }

    /**
     * 列出所有的桶
     *
     * @return 所有桶名的集合
     */
//    public List<Bucket> getAllBucketInfo() throws Exception {
//
//        //列出所有桶
//        List<Bucket> buckets = minioClient.listBuckets();
//        return buckets;
//    }

    /**
     * 列出某个桶中的所有文件名
     * 文件夹名为空时，则直接查询桶下面的数据，否则就查询当前桶下对于文件夹里面的数据
     *
     * @param bucketName 桶名称
     * @param folderName 文件夹名
     * @param isDeep     是否递归查询
     */
//    public Iterable<Result<Item>> getBucketAllFile(String bucketName, String folderName, Boolean isDeep) throws Exception {
//        if (!StringUtils.hasLength(bucketName)) {
//            throw new RuntimeException("获取桶中文件列表的时候，桶名不能为空！");
//        }
//        if (!StringUtils.hasLength(folderName)) {
//            folderName = "";
//        }
//        System.out.println(folderName);
//        Iterable<Result<Item>> listObjects = minioClient.listObjects(
//                ListObjectsArgs
//                        .builder()
//                        .bucket(bucketName)
//                        .prefix(folderName + "/")
//                        .recursive(isDeep)
//                        .build());
//
////        for (Result<Item> result : listObjects) {
////            Item item = result.get();
////            System.out.println(item.objectName());
////        }
//
//        return listObjects;
//    }

    /**
     * 删除文件夹
     *
     * @param bucketName 桶名
     * @param objectName 文件夹名
     * @param isDeep     是否递归删除
     * @return
     */
//    public Boolean deleteBucketFolder(String bucketName, String objectName, Boolean isDeep) {
//        if (!StringUtils.hasLength(bucketName) || !StringUtils.hasLength(objectName)) {
//            throw new RuntimeException("删除文件夹的时候，桶名或文件名不能为空！");
//        }
//        try {
//            ListObjectsArgs args = ListObjectsArgs.builder().bucket(bucketName).prefix(objectName + "/").recursive(isDeep).build();
//            Iterable<Result<Item>> listObjects = minioClient.listObjects(args);
//            listObjects.forEach(objectResult -> {
//                try {
//                    Item item = objectResult.get();
//                    minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(item.objectName()).build());
//                } catch (Exception e) {
//                    log.info("删除文件夹中的文件异常", e);
//                }
//            });
//            return true;
//        } catch (Exception e) {
//            log.info("删除文件夹失败");
//            return false;
//        }
//    }

    /**
     * 获取文件下载地址
     *
     * @param bucketName 桶名
     * @param objectName 文件名
     * @param expires    过期时间,默认秒
     * @return
     * @throws Exception
     */
//    public String getFileDownloadUrl(String bucketName, String objectName, Integer expires) throws Exception {
//        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
//                .method(Method.GET)//下载地址的请求方式
//                .bucket(bucketName)
//                .object(objectName)
////                .expiry(expires, TimeUnit.SECONDS)//下载地址过期时间
//                .build();
//        String objectUrl = minioClient.getPresignedObjectUrl(args);
//        return objectUrl;
//    }


//    public  String getPreviewFileUrl(String bucketName, String fileName) {
//        return minioClient.presignedGetObject(bucketName, fileName);
//    }

    /**
     * 创建文件夹
     *
     * @param bucketName 桶名
     * @param folderName 文件夹名称
     * @return
     * @throws Exception
     */
//    public ObjectWriteResponse createBucketFolder(String bucketName, String folderName) throws Exception {
//
//        if (!checkBucketExist(bucketName)) {
//            throw new RuntimeException("必须在桶存在的情况下才能创建文件夹");
//        }
//        if (!StringUtils.hasLength(folderName)) {
//            throw new RuntimeException("创建的文件夹名不能为空");
//        }
//        PutObjectArgs putObjectArgs = PutObjectArgs.builder()
//                .bucket(bucketName)
//                .object(folderName + "/")
//                .stream(new ByteArrayInputStream(new byte[0]), 0, 0)
//                .build();
//        ObjectWriteResponse objectWriteResponse = minioClient.putObject(putObjectArgs);
//
//
//        return objectWriteResponse;
//    }

}
